{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "[Index](Index.ipynb) - [Back](Widget Styling.ipynb)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "from __future__ import print_function # For py 2.7 compat"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "# Building a Custom Widget - Hello World"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The widget framework is built on top of the Comm framework (short for communication).  The Comm framework is a framework that allows you send/receive JSON messages to/from the front end (as seen below).\n",
    "\n",
    "![Widget layer](images/WidgetArch.png)\n",
    "\n",
    "To create a custom widget, you need to define the widget both in the browser and in the python kernel."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "## Building a Custom Widget"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "To get started, you'll create a simple hello world widget.  Later you'll build on this foundation to make more complex widgets."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "## Python Kernel"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### DOMWidget and Widget"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "To define a widget, you must inherit from the Widget or DOMWidget base class.  If you intend for your widget to be displayed in the Jupyter notebook, you'll want to inherit from the DOMWidget.  The DOMWidget class itself inherits from the Widget class.  The Widget class is useful for cases in which the Widget is not meant to be displayed directly in the notebook, but instead as a child of another rendering environment.  For example, if you wanted to create a three.js widget (a popular WebGL library), you would implement the rendering window as a DOMWidget and any 3D objects or lights meant to be rendered in that window as Widgets."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "### _view_name"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Inheriting from the DOMWidget does not tell the widget framework what front end widget to associate with your back end widget.  Instead, you must tell it yourself by defining a specially named traitlet, `_view_name` (as seen below)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "import ipywidgets as widgets\n",
    "from traitlets import Unicode\n",
    "\n",
    "\n",
    "class HelloWidget(widgets.DOMWidget):\n",
    "    _view_name = Unicode('HelloView').tag(sync=True)\n",
    "    _view_module = Unicode('hello').tag(sync=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "### sync=True traitlets"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Traitlets is an IPython library for defining type-safe properties on configurable objects.  For this tutorial you do not need to worry about the *configurable* piece of the traitlets machinery.   The `sync=True` keyword argument tells the widget framework to handle synchronizing that value to the browser.  Without `sync=True`, the browser would have no knowledge of `_view_name`."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "### Other traitlet types"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Unicode, used for _view_name, is not the only Traitlet type, there are many more some of which are listed below:  \n",
    "\n",
    "- Any\n",
    "- Bool\n",
    "- Bytes\n",
    "- CBool\n",
    "- CBytes\n",
    "- CComplex\n",
    "- CFloat\n",
    "- CInt\n",
    "- CLong\n",
    "- CRegExp\n",
    "- CUnicode\n",
    "- CaselessStrEnum\n",
    "- Complex\n",
    "- Dict\n",
    "- DottedObjectName\n",
    "- Enum\n",
    "- Float\n",
    "- FunctionType\n",
    "- Instance\n",
    "- InstanceType\n",
    "- Int\n",
    "- List\n",
    "- Long\n",
    "- Set\n",
    "- TCPAddress\n",
    "- Tuple\n",
    "- Type\n",
    "- Unicode\n",
    "- Union\n",
    "\n",
    "\n",
    "Not all of these traitlets can be synchronized across the network, only the JSON-able traits and Widget instances will be synchronized."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "## Front end (JavaScript)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Models and views"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The IPython widget framework front end relies heavily on [Backbone.js](http://backbonejs.org/).  Backbone.js is an MVC (model view controller) framework.  Widgets defined in the back end are automatically synchronized with generic Backbone.js models in the front end.  The traitlets are added to the front end instance automatically on first state push.  The `_view_name` trait that you defined earlier is used by the widget framework to create the corresponding Backbone.js view and link that view to the model."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "### Import the WidgetManager"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You first need to import the `jupyter-js-widgets` module. To import modules, use the `define` method of [require.js](http://requirejs.org/) (as seen below)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "application/javascript": [
       "define('hello', [\"jupyter-js-widgets\"], function(widgets) {\n",
       "    \n",
       "});"
      ],
      "text/plain": [
       "<IPython.core.display.Javascript object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "%%javascript\n",
    "define('hello', [\"jupyter-js-widgets\"], function(widgets) {\n",
    "    \n",
    "});"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "### Define the view"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Next define your widget view class.  Inherit from the `DOMWidgetView` by using the `.extend` method."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "application/javascript": [
       "require.undef('hello');\n",
       "\n",
       "define('hello', [\"jupyter-js-widgets\"], function(widgets) {\n",
       "    \n",
       "    // Define the HelloView\n",
       "    var HelloView = widgets.DOMWidgetView.extend({\n",
       "        \n",
       "    });\n",
       "\n",
       "    return {\n",
       "        HelloView: HelloView\n",
       "    }\n",
       "});"
      ],
      "text/plain": [
       "<IPython.core.display.Javascript object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "%%javascript\n",
    "require.undef('hello');\n",
    "\n",
    "define('hello', [\"jupyter-js-widgets\"], function(widgets) {\n",
    "    \n",
    "    // Define the HelloView\n",
    "    var HelloView = widgets.DOMWidgetView.extend({\n",
    "        \n",
    "    });\n",
    "\n",
    "    return {\n",
    "        HelloView: HelloView\n",
    "    }\n",
    "});"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "### Render method"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Lastly, override the base `render` method of the view to define custom rendering logic.  A handle to the widget's default div element can be acquired via `this.$el`.  The `$el` property is a [jQuery](http://jquery.com/) object handle (which can be thought of as a supercharged version of the normal DOM element's handle)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "application/javascript": [
       "require.undef('hello');\n",
       "\n",
       "define('hello', [\"jupyter-js-widgets\"], function(widgets) {\n",
       "    \n",
       "    var HelloView = widgets.DOMWidgetView.extend({\n",
       "        \n",
       "        // Render the view.\n",
       "        render: function() { \n",
       "            this.$el.text('Hello World!'); \n",
       "        },\n",
       "    });\n",
       "    \n",
       "    return {\n",
       "        HelloView: HelloView\n",
       "    };\n",
       "});"
      ],
      "text/plain": [
       "<IPython.core.display.Javascript object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "%%javascript\n",
    "require.undef('hello');\n",
    "\n",
    "define('hello', [\"jupyter-js-widgets\"], function(widgets) {\n",
    "    \n",
    "    var HelloView = widgets.DOMWidgetView.extend({\n",
    "        \n",
    "        // Render the view.\n",
    "        render: function() { \n",
    "            this.$el.text('Hello World!'); \n",
    "        },\n",
    "    });\n",
    "    \n",
    "    return {\n",
    "        HelloView: HelloView\n",
    "    };\n",
    "});"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "## Test"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You should be able to display your widget just like any other widget now."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAA9oAAAAfCAYAAAAY2rXdAAAHQElEQVR4Xu3daahtZQEG4KtgiqWoIUoDDqGWWmGzyIUrFILlBDZpJs4NRnPmlDcoibJS0SuC4jUNDDWFMEFvVFQWNBBNBFk/MlIInM3Cyt4X1oLNxrOP+7J+XDjPgpe919l7rb3Oc/ef937f+s5262wECBAgQIAAAQIECBAgQIDAZALbTXYmJyJAgAABAgQIECBAgAABAgTWKdq+BAQIECBAgAABAgQIECBAYEIBRXtCTKciQIAAAQIECBAgQIAAAQKKtu8AAQIECBAgQIAAAQIECBCYUEDRnhDTqQgQIECAAAECBAgQIECAgKLtO0CAAAECBAgQIECAAAECBCYUULQnxHQqAgQIECBAgAABAgQIECCgaPsOECBAgAABAgQIECBAgACBCQUU7QkxnYoAAQIECBAgQIAAAQIECExRtHcL4yeTtyaHJ/P726LyAbmoi5LHk4/MXeCLs//p5KDkhG3x4l0TAQIECBAgQIAAAQIECGy7Ai3a65MvJW9Kzk1uSZ5J3pVsSv6UtEhvWeHX2DE/Pz35aPLKZH5/2d/++BxwQ/JYcmLyj+Sq5B3J15ONSQvyW4b3fSePn0v+tcQH7Zz3Xj98xgfmjtsj+xcmr036nwfdLh72ez02AgQIECBAgAABAgQIECCwosA4ov2JvKNFeZ+5d/4m+3cmLbKLtmPy4leSFu1u8/vL/hP0XMclBw4HviSPDyQt/7fPnGxznp+WPLvsB+T9X01emMwX7Z7q7OGzxqLd/3w4dMhWfJRDCBAgQIAAAQIECBAgQGCtCIxF+2P5hZt9537xX2e/RXvjKiAdbb4sGYv2/P6ynq/PAb8Yiu3vh4N/kMc/J2cM+53i/fGkU8C3Zuv1vih5rqJ9Zn7+nmQs2lfk+SEz+1vzeY4hQIAAAQIECBAgQIAAgTUgsGzRfl1MOlrd0rlD0tHkR5PVinZHg89K/pm8OflM0iK9aLs/L96UfH5402/z2JHtvZNObW8Z7n8EjOfpveEt3U8Pn3FzHr+RHJZckHQK/KuTfYfH+aL99vz86KRT1o9MnkrGon1envd3fv8q1+xlAgQIECBAgAABAgQIEFjjArNFu4X2mjmPFun+bGPS0d8+P2V4z0/y+LOk928vKtqdnt0y3HvAn0h6n/PVySuSJxf4X5rXjk1a0luQ3518NmnRv3u4lg/OHH9Hnl+ZfD95adKiviHp9Pfbkt2T3kvee7s3J7NFu1PmO3L/huS/ybXD9Y1FuwW7RbuF20aAAAECBAgQIECAAAECBFYUWGZE+505S0eRW3K7tQA/knSF7kVF++S83pW9W3C7bZ90gbNO++6I80pbFyPriPWrkpbs65Iukva3pPeUt/SePxz8sjz+NekiZ+OiaC3ODyct11347KGki5yN22zR7v3ateh5u81PHW/hbtHuFHIbAQIECBAgQIAAAQIECBBYUWCZot2R645Cf+g5zraoaG/M+zsK3fuux+2XefLt5Iur/Nv8Ma9/M3l50gXKen92F0rr1PPeu/3T4fgNeexI9q5JR827tTy/Jnlb0pLeoj17P/ds0b43r92XXDIcO1+0D87PW7RvXeV6vUyAAAECBAgQIECAAAECa1xgmaJ9Uqz6Z8D6N6j/Pbh1lLrTxxcV7Y4ofy3ZM+m91d1+lXRqeKd0L9o6nb2LlbWQd1p4//RWC3Pv1+4073G18f3zvAuldXr6z4cT9jNfkPRPlq1WtLuSeaeMd1XzbvNFu9PmO2re4m8jQIAAAQIECBAgQIAAAQIrCoxF+1N5R1cdb5mc3f6QnZbQ/h3pltyW2d5v3VHl3tf8n6TTuTti3RHig4aDZ/c7yvyX5MPJt5IuWtbR445wd+GyRVtHkluqO6L99+GN381jFzbrnyOb3e4Zrm+8b/uH2e+U9d6j3aLd6erjVPMe1xHvFuhzkq4w3mnsG4Zra7Hv4mhvTPqfA0clew3vWXS9XiNAgAABAgQIECBAgACBNS7Qon1E8uWko9Mtr13pu+Wy90V3UbBO0e592FuS9UkXMmvxvTHpPc27JJcnLatd6OzHc/t3DedusW3B3inpomot8c9n25Q3zU5XPzX7Le4/mju4q5H3XuwHk66E3sXaukBap4/372D/L+miZh1N3y/pNPBey3uT3yXj6HlH6HsfeO8N/0LyveG1Tpt/3/O5YO8hQIAAAQIECBAgQIAAgbUrMI5or10BvzkBAgQIECBAgAABAgQIEJhQQNGeENOpCBAgQIAAAQIECBAgQICAou07QIAAAQIECBAgQIAAAQIEJhRQtCfEdCoCBAgQIECAAAECBAgQIKBo+w4QIECAAAECBAgQIECAAIEJBRTtCTGdigABAgQIECBAgAABAgQIKNq+AwQIECBAgAABAgQIECBAYEIBRXtCTKciQIAAAQIECBAgQIAAAQKKtu8AAQIECBAgQIAAAQIECBCYUEDRnhDTqQgQIECAAAECBAgQIECAwP8BDFLzIGhGyfsAAAAASUVORK5CYII=",
      "text/html": [
       "<img src= class=\"jupyter-widget\">\n",
       "<script type=\"application/vnd.jupyter-embedded-widgets\">[{},{\"_view_name\":\"HelloView\",\"_view_module\":\"hello\",\"layout\":\"IPY_MODEL_a2d02925ee224b789ae0285ac3bd404c\",\"_model_name\":\"DOMWidgetModel\"}]</script>"
      ]
     },
     "metadata": {
      "isWidgetSnapshot": true
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "HelloWidget()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "## Making the widget stateful"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "There is not much that you can do with the above example that you can't do with the IPython display framework.  To change this, you will make the widget stateful.  Instead of displaying a static \"hello world\" message, it will display a string set by the back end.  First you need to add a traitlet in the back end.  Use the name of `value` to stay consistent with the rest of the widget framework and to allow your widget to be used with interact."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "class HelloWidget(widgets.DOMWidget):\n",
    "    _view_name = Unicode('HelloView').tag(sync=True)\n",
    "    _view_module = Unicode('hello').tag(sync=True)\n",
    "    value = Unicode('Hello World!').tag(sync=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "### Accessing the model from the view"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "To access the model associate with a view instance, use the `model` property of the view.  `get` and `set` methods are used to interact with the Backbone model.  `get` is trivial, however you have to be careful when using `set`.  After calling the model `set` you need call the view's `touch` method.  This associates the `set` operation with a particular view so output will be routed to the correct cell.  The model also has an `on` method which allows you to listen to events triggered by the model (like value changes)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "### Rendering model contents"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "By replacing the string literal with a call to `model.get`, the view will now display the value of the back end upon display.  However, it will not update itself to a new value when the value changes."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "application/javascript": [
       "require.undef('hello');\n",
       "\n",
       "define('hello', [\"jupyter-js-widgets\"], function(widgets) {\n",
       "    \n",
       "    var HelloView = widgets.DOMWidgetView.extend({\n",
       "        \n",
       "        render: function() { \n",
       "            this.$el.text(this.model.get('value')); \n",
       "        },\n",
       "    });\n",
       "    \n",
       "    return {\n",
       "        HelloView : HelloView\n",
       "    };\n",
       "});"
      ],
      "text/plain": [
       "<IPython.core.display.Javascript object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "%%javascript\n",
    "require.undef('hello');\n",
    "\n",
    "define('hello', [\"jupyter-js-widgets\"], function(widgets) {\n",
    "    \n",
    "    var HelloView = widgets.DOMWidgetView.extend({\n",
    "        \n",
    "        render: function() { \n",
    "            this.$el.text(this.model.get('value')); \n",
    "        },\n",
    "    });\n",
    "    \n",
    "    return {\n",
    "        HelloView : HelloView\n",
    "    };\n",
    "});"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "### Dynamic updates"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "To get the view to update itself dynamically, register a function to update the view's value when the model's `value` property changes.  This can be done using the `model.on` method.  The `on` method takes three parameters, an event name, callback handle, and callback context.   The Backbone event named `change` will fire whenever the model changes.  By appending `:value` to it, you tell Backbone to only listen to the change event of the `value` property (as seen below)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "application/javascript": [
       "require.undef('hello');\n",
       "\n",
       "define('hello', [\"jupyter-js-widgets\"], function(widgets) {\n",
       "    \n",
       "    var HelloView = widgets.DOMWidgetView.extend({\n",
       "        \n",
       "        render: function() { \n",
       "            this.value_changed();\n",
       "            this.model.on('change:value', this.value_changed, this);\n",
       "        },\n",
       "        \n",
       "        value_changed: function() {\n",
       "            this.$el.text(this.model.get('value')); \n",
       "        },\n",
       "    });\n",
       "    \n",
       "    return {\n",
       "        HelloView : HelloView\n",
       "    };\n",
       "});"
      ],
      "text/plain": [
       "<IPython.core.display.Javascript object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "%%javascript\n",
    "require.undef('hello');\n",
    "\n",
    "define('hello', [\"jupyter-js-widgets\"], function(widgets) {\n",
    "    \n",
    "    var HelloView = widgets.DOMWidgetView.extend({\n",
    "        \n",
    "        render: function() { \n",
    "            this.value_changed();\n",
    "            this.model.on('change:value', this.value_changed, this);\n",
    "        },\n",
    "        \n",
    "        value_changed: function() {\n",
    "            this.$el.text(this.model.get('value')); \n",
    "        },\n",
    "    });\n",
    "    \n",
    "    return {\n",
    "        HelloView : HelloView\n",
    "    };\n",
    "});"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "## Test"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAA9oAAAAfCAYAAAAY2rXdAAAD5ElEQVR4Xu3dP6jNYRzH8XuTTfmzUTJICRkMsqhrsRn9SclRGIjEIAur8meRP1lkIJMSMliuoiSTwaDULUkxoAyUweep39mO4Xf6Tr9et9796nae5/S8epZv53bu7IwfAgQIECBAgAABAgQIECBAoExgtmwnGxEgQIAAAQIECBAgQIAAAQIzBm2XgAABAgQIECBAgAABAgQIFAoYtAsxbUWAAAECBAgQIECAAAECBAza7gABAgQIECBAgAABAgQIECgUMGgXYtqKAAECBAgQIECAAAECBAgYtN0BAgQIECBAgAABAgQIECBQKGDQLsS0FQECBAgQIECAAAECBAgQMGi7AwQIECBAgAABAgQIECBAoFDAoF2IaSsCBAgQIECAAAECBAgQIGDQdgcIECBAgAABAgQIECBAgEChwHjQ3pw9l6cXU+59IuuuTbnWMgIECBAgQIAAAQIECBAgMBiBNmgv7QbsU3nOT3GyUdYcTDumWGsJAQIECBAgQIAAAQIECBAYlEAbtI+ky+lpep7upC1pV9qYFqdD6Ue6kH6lPelxupfaJ9ntE/H76Xr6NCghhyFAgAABAgQIECBAgAABAj0Exn86vpA1ozSflqSb6UC3z6s8X6eHaW86mVZ0r7/aPdvauR7v66UECBAgQIAAAQIECBAgQGCQApMG7d056eH0rDvxpjy/p/bpdRu6z6VbaVn6atAe5L1wKAIECBAgQIAAAQIECBCYUmDSoH0me61NxybsOcrvrqSfaV96Y9CeUt4yAgQIECBAgAABAgQIEBikwKRBe39OejGtS3+6U2/L83NX+yT7dtrQ1Ybv1twghRyKAAECBAgQIECAAAECBAj0EBgP2h+y5mx6n76lj+ltupTWpL/pS1qU2pemrU7ti9PWpzaYt7Vb08q00OP9vZQAAQIECBAgQIAAAQIECAxKYDxon8+pjqbj6VHanto3iLeB+m46nXamB+lGat9E/iS1/7u9Kr1M71L7c/LfgxJyGAIECBAgQIAAAQIECBAg0ENgPGj3WOKlBAgQIECAAAECBAgQIECAwP8EDNruBgECBAgQIECAAAECBAgQKBQwaBdi2ooAAQIECBAgQIAAAQIECBi03QECBAgQIECAAAECBAgQIFAoYNAuxLQVAQIECBAgQIAAAQIECBAwaLsDBAgQIECAAAECBAgQIECgUMCgXYhpKwIECBAgQIAAAQIECBAgYNB2BwgQIECAAAECBAgQIECAQKGAQbsQ01YECBAgQIAAAQIECBAgQMCg7Q4QIECAAAECBAgQIECAAIFCAYN2IaatCBAgQIAAAQIECBAgQIDAP+MSRSA4tnQ5AAAAAElFTkSuQmCC",
      "text/html": [
       "<img src= class=\"jupyter-widget\">\n",
       "<script type=\"application/vnd.jupyter-embedded-widgets\">[{},{\"_view_name\":\"HelloView\",\"_view_module\":\"hello\",\"layout\":\"IPY_MODEL_72ec75fcf53043af9f86620299e17662\",\"_model_name\":\"DOMWidgetModel\",\"value\":\"test\"}]</script>"
      ]
     },
     "metadata": {
      "isWidgetSnapshot": true
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "w = HelloWidget()\n",
    "w"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "w.value = 'test'"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "## Finishing"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Bidirectional communication"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The examples above dump the value directly into the DOM.  There is no way for you to interact with this dumped data in the front end.  To create an example that accepts input, you will have to do something more than blindly dumping the contents of value into the DOM.  In this part of the tutorial, you will use a jQuery spinner to display and accept input in the front end."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "#### Update the Python code"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You will need to change the type of the value traitlet to `Int`.  It also makes sense to change the name of the widget to something more appropriate, like `SpinnerWidget`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "from traitlets import CInt\n",
    "\n",
    "\n",
    "class SpinnerWidget(widgets.DOMWidget):\n",
    "    _view_name = Unicode('SpinnerView').tag(sync=True)\n",
    "    _view_module = Unicode('spinner').tag(sync=True)\n",
    "    value = CInt().tag(sync=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "#### Updating the Javascript code"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The [jQuery docs for the spinner control](https://jqueryui.com/spinner/) say to use `.spinner` to create a spinner in an element.  Calling `.spinner` on `$el` will create a spinner inside `$el`.  Make sure to update the widget name here too so it's the same as `_view_name` in the back end."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "application/javascript": [
       "define('spinner', [\"jupyter-js-widgets\"], function(widgets) {\n",
       "    \n",
       "    var SpinnerView = widgets.DOMWidgetView.extend({\n",
       "        \n",
       "        render: function() { \n",
       "            \n",
       "            // jQuery code to create a spinner and append it to $el\n",
       "            this.$input = $('<input />');\n",
       "            this.$el.append(this.$input);\n",
       "            this.$spinner = this.$input.spinner({\n",
       "                change: function( event, ui ) {}\n",
       "            });\n",
       "            \n",
       "            this.value_changed();\n",
       "            this.model.on('change:value', this.value_changed, this);\n",
       "        },\n",
       "        \n",
       "        value_changed: function() {\n",
       "            \n",
       "        },\n",
       "    });\n",
       "    \n",
       "    return {\n",
       "        SpinnerView: SpinnerView\n",
       "    };\n",
       "});"
      ],
      "text/plain": [
       "<IPython.core.display.Javascript object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "%%javascript\n",
    "define('spinner', [\"jupyter-js-widgets\"], function(widgets) {\n",
    "    \n",
    "    var SpinnerView = widgets.DOMWidgetView.extend({\n",
    "        \n",
    "        render: function() { \n",
    "            \n",
    "            // jQuery code to create a spinner and append it to $el\n",
    "            this.$input = $('<input />');\n",
    "            this.$el.append(this.$input);\n",
    "            this.$spinner = this.$input.spinner({\n",
    "                change: function( event, ui ) {}\n",
    "            });\n",
    "            \n",
    "            this.value_changed();\n",
    "            this.model.on('change:value', this.value_changed, this);\n",
    "        },\n",
    "        \n",
    "        value_changed: function() {\n",
    "            \n",
    "        },\n",
    "    });\n",
    "    \n",
    "    return {\n",
    "        SpinnerView: SpinnerView\n",
    "    };\n",
    "});"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "#### Getting and setting the value"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "To set the value of the spinner on update from the back end, you need to use jQuery's `spinner` API.  `spinner.spinner('value', new)` will set the value of the spinner.  Add that code to the `value_changed` method to make the spinner update with the value stored in the back end.  Using jQuery's spinner API, you can add a function to handle the spinner `change` event by passing it in when constructing the spinner.  Inside the `change` event, call `model.set` to set the value and then `touch` to inform the framework that this view was the view that caused the change to the model.  Note: The `var that = this;` is a JavaScript trick to pass the current context into closures."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "application/javascript": [
       "requirejs.undef('spinner');\n",
       "\n",
       "define('spinner', [\"jupyter-js-widgets\"], function(widgets) {\n",
       "\n",
       "    var SpinnerView = widgets.DOMWidgetView.extend({\n",
       "        render: function() { \n",
       "\n",
       "            var that = this;\n",
       "            this.$input = $('<input />');\n",
       "            this.$el.append(this.$input);\n",
       "            this.$spinner = this.$input.spinner({\n",
       "                change: function( event, ui ) {\n",
       "                    that.handle_spin(that.$spinner.spinner('value'));\n",
       "                },\n",
       "                spin: function( event, ui ) {\n",
       "                    //ui.value is the new value of the spinner\n",
       "                    that.handle_spin(ui.value);\n",
       "                }\n",
       "            });\n",
       "            \n",
       "            this.value_changed();\n",
       "            this.model.on('change:value', this.value_changed, this);\n",
       "        },\n",
       "        \n",
       "        value_changed: function() {\n",
       "            this.$spinner.spinner('value', this.model.get('value'));\n",
       "        },\n",
       "        \n",
       "        handle_spin: function(value) {\n",
       "            this.model.set('value', value);\n",
       "            this.touch();\n",
       "        },\n",
       "    });\n",
       "    \n",
       "    return {\n",
       "        SpinnerView: SpinnerView\n",
       "    };\n",
       "});"
      ],
      "text/plain": [
       "<IPython.core.display.Javascript object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "%%javascript\n",
    "requirejs.undef('spinner');\n",
    "\n",
    "define('spinner', [\"jupyter-js-widgets\"], function(widgets) {\n",
    "\n",
    "    var SpinnerView = widgets.DOMWidgetView.extend({\n",
    "        render: function() { \n",
    "\n",
    "            var that = this;\n",
    "            this.$input = $('<input />');\n",
    "            this.$el.append(this.$input);\n",
    "            this.$spinner = this.$input.spinner({\n",
    "                change: function( event, ui ) {\n",
    "                    that.handle_spin(that.$spinner.spinner('value'));\n",
    "                },\n",
    "                spin: function( event, ui ) {\n",
    "                    //ui.value is the new value of the spinner\n",
    "                    that.handle_spin(ui.value);\n",
    "                }\n",
    "            });\n",
    "            \n",
    "            this.value_changed();\n",
    "            this.model.on('change:value', this.value_changed, this);\n",
    "        },\n",
    "        \n",
    "        value_changed: function() {\n",
    "            this.$spinner.spinner('value', this.model.get('value'));\n",
    "        },\n",
    "        \n",
    "        handle_spin: function(value) {\n",
    "            this.model.set('value', value);\n",
    "            this.touch();\n",
    "        },\n",
    "    });\n",
    "    \n",
    "    return {\n",
    "        SpinnerView: SpinnerView\n",
    "    };\n",
    "});"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "## Test"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/html": [
       "<img src= class=\"jupyter-widget\">\n",
       "<script type=\"application/vnd.jupyter-embedded-widgets\">[{},{\"_view_name\":\"SpinnerView\",\"_view_module\":\"spinner\",\"layout\":\"IPY_MODEL_c4f82433123f464183e64de8c56a3321\",\"_model_name\":\"DOMWidgetModel\",\"value\":20}]</script>"
      ]
     },
     "metadata": {
      "isWidgetSnapshot": true
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "w = SpinnerWidget(value=5)\n",
    "w"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "5"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "w.value"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "w.value = 20"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Trying to use the spinner with another widget."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/html": [
       "<img src= class=\"jupyter-widget\">\n",
       "<script type=\"application/vnd.jupyter-embedded-widgets\">[{},{\"_view_name\":\"SpinnerView\",\"_view_module\":\"spinner\",\"layout\":\"IPY_MODEL_5e1967a8ebf1409c83eba91a556d4ba3\",\"_model_name\":\"DOMWidgetModel\",\"value\":0},{},{\"layout\":\"IPY_MODEL_404808cb221a417e92f6b64ea1a9f56a\"}]</script>"
      ]
     },
     "metadata": {
      "isWidgetSnapshot": true
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "from IPython.display import display\n",
    "w1 = SpinnerWidget(value=0)\n",
    "w2 = widgets.IntSlider()\n",
    "display(w1,w2)\n",
    "\n",
    "from traitlets import link\n",
    "mylink = link((w1, 'value'), (w2, 'value'))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "[Index](Index.ipynb) - [Back](Widget Styling.ipynb)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.4.3+"
  },
  "widgets": {
   "state": {
    "058d928af52e47af8524228a5956aee0": {
     "views": [
      {
       "cell": {
        "cell_type": "code",
        "execution_count": 15,
        "metadata": {
         "collapsed": false,
         "trusted": true
        },
        "outputs": [
         {
          "data": {
           "image/png": "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",
           "text/html": "<img src= class=\"jupyter-widget\">\n<script type=\"application/vnd.jupyter-embedded-widgets\">[{},{\"_view_name\":\"SpinnerView\",\"_view_module\":\"spinner\",\"layout\":\"IPY_MODEL_c4f82433123f464183e64de8c56a3321\",\"_model_name\":\"DOMWidgetModel\",\"value\":20}]</script>"
          },
          "metadata": {
           "isWidgetSnapshot": true
          },
          "output_type": "display_data"
         }
        ],
        "source": "w = SpinnerWidget(value=5)\nw"
       },
       "cell_index": 58,
       "root": true
      }
     ]
    },
    "3ab009d901ce4c4a8c5f63e6bfa12202": {
     "views": [
      {
       "cell": {
        "cell_type": "code",
        "execution_count": 18,
        "metadata": {
         "collapsed": false,
         "trusted": true
        },
        "outputs": [
         {
          "data": {
           "image/png": "iVBORw0KGgoAAAANSUhEUgAAA9oAAABLCAYAAACPxf88AAAL5UlEQVR4Xu3dT2hV2R0H8BOd4BRRM6Vqk1BBihujmSqOG7VWunDTRaX1T9WVuBmp0gFbi64Ko910FCmOLqwgDG2ZGZiRgn8W6kCEomhVoiAqtWbMBGzTpP7BP1Ff7w2NnTdibjLm3rx77+fBDwPveM75fc518fXevFcXvAgQIECAAAECBAgQIECAAIERE6gbsZlMRIAAAQIECBAgQIAAAQIECARB20VAgAABAgQIECBAgAABAgRGUEDQHkFMUxEgQIAAAQIECBAgQIAAAUHbNUCAAAECBAgQIECAAAECBEZQoCpoX758uTKCc5uKQGEEWlpa/KdUYU5TIwQIECBAgAABAgTSFegPD2fPnq10d3eH5ubmdFczO4GcCnR2dobJkyeHuXPnCtw5PUPbJkCAAAECBAgQIJCVQH9oaGtrqyxcuDCrNa1DIJcCp06dCosWLRK0c3l6Nk2AAAECBAgQIEAgO4G6Y8eOVebPnx8aGhqyW9VKBHIo0NvbG86cOROWLl0qbOfw/GyZAAECBAgQIECAQFYCdR9++GFl+fLlWa1nHQK5Fvjoo4/CihUrBO1cn6LNEyBAgAABAgQIEEhXQNBO19fsBRMQtAt2oNohQIAAAQIECBAgkIKAoJ0CqinzJ9DV1RWmTZsWOjo6QmNj40sbELTzd7Z2TIAAAQIECBAgQCBrAUE7a3Hr1ZzAQMge2NhgYVvQrrnjsyECBAgQIECAAAECNScwpKDd09MTduzYEY4fPx4ePnwYWltbw7Zt28Ls2bNrriEbIjAcga+G7KSwLWgPR9dYAgQIECBAgAABAuUUGFLQXrVqVbh582bYtGlTmDRpUjh48GC4evVqOHr0aJg6dWo55XRdCIH6+vqX9tHX1/fCe4J2IY5dEwQIECBAgAABAgRSFUgM2mfPng0rV64MBw4cCIsXL+7fzN27d+PvEw5r164NmzdvTnWDJidQSwKCdi2dhr0QIECAAAECBAgQqE2BxKC9e/fusG/fvnDp0qUwduzY511s2LAhfPHFF+HTTz+tzc7sikAKAoJ2CqimJECAAAECBAgQIFAwgcSg/c4774T4rnZbW1tV6++++274+OOPw4ULFwpGoh0CLxcQtF0dBAgQIECAAAECBAgkCSQG7fXr14fPP/88HDt2rGquXbt2hT179oTr168nreF9AoURELQLc5QaIUCAAAECBAgQIJCawJCCdvyI+OHDh6s2sXPnzvD+++8L2qkdjYlrUUDQrsVTsScCBAgQIECAAAECtSWQGLTjR8cvXrwYTpw4UbVzj47X1kHaTTYCgnY2zlYhQIAAAQIECBAgkGeBxKAdfxja3r17+8P2uHHjnve6bt260N3dHQ4dOpTn/u2dwLAEBO1hcRlMgAABAgQIECBAoJQCiUH79OnTYfXq1WH//v1hyZIl/Ujx13stWLAgrFmzJmzZsqWUcJoup4CgXc5z1zUBAgQIECBAgACB4QgkBu14suXLl4dbt26FjRs3hgkTJoQPPvggXLlyJRw5ciQ0NTUNZz1jCeRaQNDO9fHZPAECBAgQIECAAIFMBIYUtONHxLdv3x5OnjwZHj58GFpbW8PWrVvDm2++mckmLUIgLYH6+vqXTt3X1/fCe4J2WidhXgIECBAgQIAAAQLFERhS0C5OuzohUC3Q1dUVpk2b9gJLR0dHaGxsFLRdMAQIECBAgAABAgQIDFtA0B42mb9QNIGvhu2Xhey4b3e0i3b6+iFAgAABAgQIECAw8gKC9sibmjGHAgNhe7CQLWjn8GBtmQABAgQIECBAgMAoCAjao4BuyfwKuKOd37OzcwIECBAgQIAAAQJZCQjaWUlbpxACgnYhjlETBAgQIECAAAECBFIVqIs+SbwyY8aM0NzcnOpCJieQd4HOzs5w+fLlsHTp0rq892L/BAgQIECAAAECBAikJ9AfGNra2ipz5swJ48ePT28lMxPIscD9+/fD+fPnw6JFi4TsHJ+jrRMgQIAAAQIECBDIQuB5aPjkk08qDQ0NYcqUKVmsaw0CuRG4fft26O3tDcuWLROyc3NqNkqAAAECBAgQIEBg9AQEh9GztzIBAgQIECBAgAABAgQIFFBA0C7goWqJAAECBAgQIECAAAECBEZPQNAePXsrEyBAgAABAgQIECBAgEABBQTtAh6qlggQIECAAAECBAgQIEBg9AQE7dGztzIBAgQIECBAgAABAgQIFFCgKmhH3xH8vadPn04arM8JEyZcnD59em8BLbREgAABAgQIECBAgAABAgReWeB50L527Vrl0aNHoa5u8JvclUolHvP9lpaWtlde3QQECBAgQIAAAQIECBAgQKBgAv2pOr6TPWbMmPNRJbb37NmzEFcUtD12nqhlAAECBAgQIECAAAECBAiUTaA/LLe3t/+gvr7+ZNLd7HisoF22S0S/BAgQIECAAAECBAgQIDAcAUF7OFrGEiBAgAABAgQIECBAgACBBAFB2yVCgAABAgQIECBAgAABAgRGUEDQHkFMUxEgQIAAAQIECBAgQIAAAUHbNUCAwKsKfCOa4L2o/hPVN6P6ZVR3XnVSf58AAQIECBAgQIBAXgUE7byenH0TqB2B/dFW/hrVH6L6aVQ/iepntbM9OyFAgAABAgQIECCQrYCgna231QgUTeDbUUOdUcV//jOqiVH1RPXdqP5RtGb1Q4AAAQIECBAgQGAoAoL2UJSMIUDgZQLxnesDUcWPjw+8uqIffh3VQWwECBAgQIAAAQIEyiggaJfx1PVMYOQE4t/H/kVUzV+a8tr/wvdvR24ZMxEgQIAAAQIECBDIj8DXDtrjx4//W37atFMC5RHo6en54Zw5c3oz6nhztM7bUcWPig+8bkY//D6q32W0B8sQIECAAAECBAgQqCmBrx20GxoaaqoRmyFAIITHjx+HJ0+ehBkzZvT/287gFT86Hofqb31prX9HP/88qj9msL4lCBAgQIAAAQIECNScwNcO2jNnzqy5ZmyIAIEQzp07F+bNm5dV0I4/BK0jqsaouqOKv97rX1F9J6r4Q9K8CBAgQIAAAQIECJROQNAu3ZFruMgCT58+DRcuXMgyaMecf4rqUFR/jmpFVD+OanWRnfVGgAABAgQIECBAYDCBqqB9586dRK0xY8aE+vr6MHny5MSxBhAgkK1A/G944sSJoampKas72nGDb0T1XlR/j6opql9FdS/bzq1GgAABAgQIECBAoHYEqoL23bt3E3cWB+3XXnst/l3Q3yQONoAAgawFPnvrrbc+y3pR6xEgQIAAAQIECBAg8H+BqqB9717yTaiBoN3a2prlHTNnRoAAAQIECBAgQIAAAQIEciFQFbTv37+fuOm6urr+O9qCdiKVAQQIECBAgAABAgQIECBQQoGqoP3gwYNEgjhox3e1Be1EKgMIECBAgAABAgQIECBAoIQCVUH70aNHQyKIg/bs2bM9Oj4kLYMIECBAgAABAgQIECBAoEwCVUG7r69vyL0L2kOmMpAAAQIECBAgQIAAAQIESiRQFbTj7+BNelUqlf4hs2bNckc7Ccv7BAgQIECAAAECBAgQIFA6gaqgPRCiB1OIxzx79kzQLt2lomECBAgQIECAAAECBAgQGIpAVdCOP+gs6RWH7LhaWlqSBydN5n0CBAgQIECAAAECBAgQIFAwgaqgPXbs2MT24pD95MkTQTtRygACBAgQIECAAAECBAgQKKNAf9C+ceNGQ/TVXj1x0E66qx3/Hnf8+PjMmTPd0S7jFaNnAgQIECBAgAABAgQIEBhU4HlYbm9v/1EUtP+S5BUH8ddff/2N6dOn9yaN9T4BAgQIECBAgAABAgQIECibgLvSZTtx/RIgQIAAAQIECBAgQIBAqgKCdqq8JidAgAABAgQIECBAgACBsgkI2mU7cf0SIECAAAECBAgQIECAQKoCgnaqvCYnQIAAAQIECBAgQIAAgbIJCNplO3H9EiBAgAABAgQIECBAgECqAoJ2qrwmJ0CAAAECBAgQIECAAIGyCQjaZTtx/RIgQIAAAQIECBAgQIBAqgKCdqq8JidAgAABAgQIECBAgACBsgn8F6ol02rUt7IIAAAAAElFTkSuQmCC",
           "text/html": "<img src= class=\"jupyter-widget\">\n<script type=\"application/vnd.jupyter-embedded-widgets\">[{},{\"_view_name\":\"SpinnerView\",\"_view_module\":\"spinner\",\"layout\":\"IPY_MODEL_5e1967a8ebf1409c83eba91a556d4ba3\",\"_model_name\":\"DOMWidgetModel\",\"value\":0},{},{\"layout\":\"IPY_MODEL_404808cb221a417e92f6b64ea1a9f56a\"}]</script>"
          },
          "metadata": {
           "isWidgetSnapshot": true
          },
          "output_type": "display_data"
         }
        ],
        "source": "from IPython.display import display\nw1 = SpinnerWidget(value=0)\nw2 = widgets.IntSlider()\ndisplay(w1,w2)\n\nfrom traitlets import link\nmylink = link((w1, 'value'), (w2, 'value'))"
       },
       "cell_index": 62,
       "root": true
      }
     ]
    },
    "404808cb221a417e92f6b64ea1a9f56a": {
     "views": []
    },
    "5e1967a8ebf1409c83eba91a556d4ba3": {
     "views": []
    },
    "628e7df013c046e8a540c4b2cf6c2e07": {
     "views": [
      {
       "cell": {
        "cell_type": "code",
        "execution_count": 18,
        "metadata": {
         "collapsed": false,
         "trusted": true
        },
        "outputs": [
         {
          "data": {
           "image/png": "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",
           "text/html": "<img src= class=\"jupyter-widget\">\n<script type=\"application/vnd.jupyter-embedded-widgets\">[{},{\"_view_name\":\"SpinnerView\",\"_view_module\":\"spinner\",\"layout\":\"IPY_MODEL_5e1967a8ebf1409c83eba91a556d4ba3\",\"_model_name\":\"DOMWidgetModel\",\"value\":0},{},{\"layout\":\"IPY_MODEL_404808cb221a417e92f6b64ea1a9f56a\"}]</script>"
          },
          "metadata": {
           "isWidgetSnapshot": true
          },
          "output_type": "display_data"
         }
        ],
        "source": "from IPython.display import display\nw1 = SpinnerWidget(value=0)\nw2 = widgets.IntSlider()\ndisplay(w1,w2)\n\nfrom traitlets import link\nmylink = link((w1, 'value'), (w2, 'value'))"
       },
       "cell_index": 62,
       "root": true
      }
     ]
    },
    "72ec75fcf53043af9f86620299e17662": {
     "views": []
    },
    "7ebc8b99beed46f18af332aa7f2cb641": {
     "views": [
      {
       "cell": {
        "cell_type": "code",
        "execution_count": 10,
        "metadata": {
         "collapsed": false,
         "trusted": true
        },
        "outputs": [
         {
          "data": {
           "image/png": "iVBORw0KGgoAAAANSUhEUgAAA9oAAAAfCAYAAAAY2rXdAAAD5ElEQVR4Xu3dP6jNYRzH8XuTTfmzUTJICRkMsqhrsRn9SclRGIjEIAur8meRP1lkIJMSMliuoiSTwaDULUkxoAyUweep39mO4Xf6Tr9et9796nae5/S8epZv53bu7IwfAgQIECBAgAABAgQIECBAoExgtmwnGxEgQIAAAQIECBAgQIAAAQIzBm2XgAABAgQIECBAgAABAgQIFAoYtAsxbUWAAAECBAgQIECAAAECBAza7gABAgQIECBAgAABAgQIECgUMGgXYtqKAAECBAgQIECAAAECBAgYtN0BAgQIECBAgAABAgQIECBQKGDQLsS0FQECBAgQIECAAAECBAgQMGi7AwQIECBAgAABAgQIECBAoFDAoF2IaSsCBAgQIECAAAECBAgQIGDQdgcIECBAgAABAgQIECBAgEChwHjQ3pw9l6cXU+59IuuuTbnWMgIECBAgQIAAAQIECBAgMBiBNmgv7QbsU3nOT3GyUdYcTDumWGsJAQIECBAgQIAAAQIECBAYlEAbtI+ky+lpep7upC1pV9qYFqdD6Ue6kH6lPelxupfaJ9ntE/H76Xr6NCghhyFAgAABAgQIECBAgAABAj0Exn86vpA1ozSflqSb6UC3z6s8X6eHaW86mVZ0r7/aPdvauR7v66UECBAgQIAAAQIECBAgQGCQApMG7d056eH0rDvxpjy/p/bpdRu6z6VbaVn6atAe5L1wKAIECBAgQIAAAQIECBCYUmDSoH0me61NxybsOcrvrqSfaV96Y9CeUt4yAgQIECBAgAABAgQIEBikwKRBe39OejGtS3+6U2/L83NX+yT7dtrQ1Ybv1twghRyKAAECBAgQIECAAAECBAj0EBgP2h+y5mx6n76lj+ltupTWpL/pS1qU2pemrU7ti9PWpzaYt7Vb08q00OP9vZQAAQIECBAgQIAAAQIECAxKYDxon8+pjqbj6VHanto3iLeB+m46nXamB+lGat9E/iS1/7u9Kr1M71L7c/LfgxJyGAIECBAgQIAAAQIECBAg0ENgPGj3WOKlBAgQIECAAAECBAgQIECAwP8EDNruBgECBAgQIECAAAECBAgQKBQwaBdi2ooAAQIECBAgQIAAAQIECBi03QECBAgQIECAAAECBAgQIFAoYNAuxLQVAQIECBAgQIAAAQIECBAwaLsDBAgQIECAAAECBAgQIECgUMCgXYhpKwIECBAgQIAAAQIECBAgYNB2BwgQIECAAAECBAgQIECAQKGAQbsQ01YECBAgQIAAAQIECBAgQMCg7Q4QIECAAAECBAgQIECAAIFCAYN2IaatCBAgQIAAAQIECBAgQIDAP+MSRSA4tnQ5AAAAAElFTkSuQmCC",
           "text/html": "<img src= class=\"jupyter-widget\">\n<script type=\"application/vnd.jupyter-embedded-widgets\">[{},{\"_view_name\":\"HelloView\",\"_view_module\":\"hello\",\"layout\":\"IPY_MODEL_72ec75fcf53043af9f86620299e17662\",\"_model_name\":\"DOMWidgetModel\",\"value\":\"test\"}]</script>"
          },
          "metadata": {
           "isWidgetSnapshot": true
          },
          "output_type": "display_data"
         }
        ],
        "source": "w = HelloWidget()\nw"
       },
       "cell_index": 43,
       "root": true
      }
     ]
    },
    "a2d02925ee224b789ae0285ac3bd404c": {
     "views": []
    },
    "c4f82433123f464183e64de8c56a3321": {
     "views": []
    },
    "d7f97fbca6ce4cb1b6f26f48a974331e": {
     "views": [
      {
       "cell": {
        "cell_type": "code",
        "execution_count": 6,
        "metadata": {
         "collapsed": false,
         "trusted": true
        },
        "outputs": [
         {
          "data": {
           "image/png": "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",
           "text/html": "<img src= class=\"jupyter-widget\">\n<script type=\"application/vnd.jupyter-embedded-widgets\">[{},{\"_view_name\":\"HelloView\",\"_view_module\":\"hello\",\"layout\":\"IPY_MODEL_a2d02925ee224b789ae0285ac3bd404c\",\"_model_name\":\"DOMWidgetModel\"}]</script>"
          },
          "metadata": {
           "isWidgetSnapshot": true
          },
          "output_type": "display_data"
         }
        ],
        "source": "HelloWidget()"
       },
       "cell_index": 30,
       "root": true
      }
     ]
    }
   },
   "version": "0.0.1"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
